package edu.amm.fanal.api;

/**
 * Краевая задача для линейного обыкновенного дифференциального
 * уравнения второго порядка вида:
 * <br>
 * <center><b>x''  +  p(t) x' + q(t) x = f(t)</b></center>
 * 
 * @author Иванов Илья
 * @since 2013-05-10
 *
 */
public class LinearProblem extends BoundaryProblem {
	
	private Function p = Function.createIdentical(SOLUTION_DIM, 0);
	private Function q = Function.createIdentical(SOLUTION_DIM, 0);
	private Function f = Function.createIdentical(SOLUTION_DIM, 0);
	
	/**
	 * @param leftBound Левая граница задачи.
	 * @param leftValue Значение решения на левой границе.
	 * @param rightBound Правая граница задачи.
	 * @param rightValue Значение решения на правой границе.
	 */
	public LinearProblem(double leftBound, double leftValue, double rightBound, double rightValue) {
		super(leftBound, leftValue, rightBound, rightValue);
	}
	
	/**
	 * @param p Коэффициент перед первой производной.
	 */
	public void setP(Function p) {
		Function.checkFunction(p, "p", SOLUTION_DIM);
		this.p = p;
	}
	
	/**
	 * @return Коэффициент перед первой производной. По умолчанию тождественно равен нулю.
	 */
	public Function getP() {
		return p;
	}
	
	/**
	 * @param q Коэффициент перед нулевой производной.
	 */
	public void setQ(Function q) {
		Function.checkFunction(q, "q", SOLUTION_DIM);
		this.q = q;
	}
	
	/**
	 * @return Коэффициент перед нулевой производной. По умолчанию тождественно равен нулю.
	 */
	public Function getQ() {
		return q;
	}
	
	/**
	 * @param f Правая часть уравнения.
	 */
	public void setF(Function f) {
		Function.checkFunction(f, "f", SOLUTION_DIM);
		this.f = f;
	}
	
	/**
	 * @return Правая часть уравнения. По умолчанию тождественно равна нулю.
	 */
	public Function getF() {
		return f;
	}
	
	/**
	 * Применяет к элементу функционального пространтсва
	 * оператор, задающий левую часть уравнения:<br>
	 * 
	 * Ax = x'' + p(t) * x' + q(t) * x
	 * 
	 * @param elem Элемент функционального пространства.
	 * @return Значение линейного оператора на элементе.
	 */
	public Function applyLeftHandOperator(final Function elem) {
		if (elem == null)
			throw new RuntimeException("Не задан элемент для вычисления оператора");
		
		return new Function(elem.getDim()) {
			
			private Function delem = elem.differentiate(0);
			private Function d2elem = delem.differentiate(0);
			private Function p = getP();
			private Function q = getQ();
			
			protected double getValue(double... x) {
				return d2elem.evaluate(x) + p.evaluate(x) * delem.evaluate(x) + q.evaluate(x) * elem.evaluate(x);
			}
		};
	}
}